home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Games / flying-6.11 / pball.C < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-30  |  13.9 KB  |  670 lines

  1. #ifndef _global_h
  2. #    include "global.h"
  3. #endif
  4.  
  5. #ifndef _pball_h
  6. #    include "pball.h"
  7. #endif
  8. #ifndef _ball_h
  9. #    include "ball.h"
  10. #endif
  11. #ifndef _graph_h
  12. #    include "graph.h"
  13. #endif
  14. #ifndef _game_h
  15. #    include "game.h"
  16. #endif
  17.  
  18. #define    SHOW_MODE        1
  19. #ifndef STATISTICS
  20. #    if (SHOW_MODE)
  21. #        undef  SHOW_MODE
  22. #        define SHOW_MODE    0
  23. #    endif
  24. #endif
  25. #define    NEW_FACTOR        2.0
  26.  
  27. static const int OLD = 1;
  28. static const int NEW = 0;
  29.  
  30. PBallTop *PBallTop::pball_queue=0l;
  31. int PBallTop::id_count = 0;
  32.  
  33. PBallTop::PBallTop(PBallType type):
  34. aim_hint(1), hint_valid(0)
  35. {
  36.     PBallTop::id = PBallTop::id_count++;
  37.     next          = pball_queue;
  38.     pball_queue    = this;
  39.  
  40.     pball_type    = type;            /* 0-Queue 1-DiscSlider 2-DiscThrower */
  41.     target_time    = NO_TARGET;
  42.  
  43.     cue = 0l;
  44.     mode = Unlocked;
  45.     default_cue = new Ball( 0.0, 0.0, 0.0, 0.0, 0.1 );
  46.  
  47.     tool_is_visible      = 0;
  48.     valid_queue_position = 0;
  49. }
  50.  
  51.  
  52. PBallTop::~PBallTop() {
  53. PBallTop    *last;
  54. //
  55. // Pointer aus Listen entfernen
  56. //
  57.     if (pball_queue==this)        pball_queue = next;
  58.     else {
  59.         last = pball_queue;
  60.         while ( last->next && last->next != this )      last = last->next;
  61.         last->next = next;
  62.     }
  63. //
  64. // eigenes aufrΣumen
  65. //
  66.     if (default_cue)        delete default_cue;
  67. }
  68.  
  69.  
  70. void PBallTop::CueMoved() {
  71.     if (mode&UsingTool)            MoveAimingTool();
  72. }
  73.  
  74. void PBallTop::CueWasHit() {
  75. //
  76. // Der cueball war an einer Kollision beteiligt und verΣnderte seine Richtung
  77. // Im PullMoving-Mode bedeutet dies, das die Maus an eine andere Stelle
  78. // gewarpt werden mu▀, damit der cueball immer noch kontinuierlich in
  79. // Mausrichtung gezogen wird.
  80. //
  81.     if (mode&PullMoving) {
  82. #ifndef __TURBOC__
  83.         if (target_time!=NO_TARGET) {
  84.             dest = cue->P()+cue->V()*(target_time-current_time);
  85.             Warp( dest );            // internen X-Pointer verschieben
  86.             SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
  87.                                         // Darstellung verschieben
  88.         }
  89. #endif
  90.     }
  91. }
  92.  
  93.  
  94. void PBallTop::WarpRecalc() {
  95.     if (dest!=cue->P()) {
  96.         target_time = GetCurrentTime() + WarpTime;    // Offset bis zum Zielpunkt
  97.         cue->ChgV(((dest-cue->P())/(target_time-current_time)));
  98.  
  99. #ifdef DEBUG
  100.         if (debug&PointerMove) {
  101.         printf( "PointerMove: P()=(%g,%g), V()=(%5.1f,%5.1f), dest=(%g, %g) (in %g secs)\n",
  102.                     (double)cue->PX(), (double)cue->PY(),
  103.                     (double)cue->VX(), (double)cue->VY(),
  104.                     (double)dest.X(), (double)dest.Y(),
  105.                     (double)(target_time-current_time) );
  106.         }
  107. #endif
  108.     }
  109.     else {
  110.         target_time = NO_TARGET;
  111. #ifdef DEBUG
  112.         if (debug&PointerMove) {
  113.             printf( "PointerMove: P()=(%g,%g) - destination reached\n",
  114.                         (double)cue->PX(), (double)cue->PY() );
  115.         }
  116. #endif
  117.     }
  118. }
  119.  
  120. void PBallTop::PointerMoveTo( const Vec2 &pointer_position ) {
  121.     dest = pointer_position;
  122.     SetPointer( (int)(dest.X()*w2n), (int)(dest.Y()*w2n) );
  123.     if (mode&UsingTool)                MoveAimingTool();
  124.     else if (mode&Moving)            cue->SetP(dest);
  125.     else if (mode&PullMoving) {
  126.         WarpRecalc();
  127.     }
  128. }
  129.  
  130.  
  131. void PBallTop::ForAll( PBallFun fun ) {
  132.     for (PBallTop *pball=pball_queue; pball; pball=pball->next)    (pball->*fun)();
  133. }
  134.  
  135.  
  136. void PBallTop::Press(int button) {
  137.     switch(button) {
  138.     case 1:
  139.         switch(pball_type) {
  140.         case BillardQueue:
  141.             switch(mode) {
  142.             case Aiming:                StartCharging(OLD);    break;
  143.             case Charging:                StartShooting(NEW);    break;
  144.             case Unlocked:                StartAiming();            break;
  145.             default:                        break;
  146.             }
  147.             break;
  148.         case DiscSlider:
  149.         case DiscThrower:
  150.             if (mode!=PullMoving)    StartPullMoving();
  151.             break;
  152.         }
  153.         break;
  154.  
  155. #if (0)
  156.     case 2:        // Middle Button
  157.         if (PBallTop::id==0) {
  158.             CloseGraphic();
  159.             exit(0);
  160.         }
  161.         break;
  162. #endif
  163.  
  164.     case 3:        // Right Button
  165.         switch(mode) {
  166.         case Unlocked:
  167. #if (1)
  168.                                             StartPullMoving();
  169. #else
  170.         // PullMoving is not yet debugging in the DOS-version
  171.                                             StartMoving();
  172. #endif
  173.                                             break;
  174.         case Aiming:                    StartCharging(NEW);    break;
  175.         default:
  176.             break;
  177.         }
  178.         break;    // case 3 - Right Button
  179.     }
  180. }
  181.  
  182.  
  183. void PBallTop::Release(int button) {
  184.     switch(button) {
  185.     case 1:        // Left Button
  186.         switch(mode) {
  187.         case LockedQueue:                GoUnlocked();                break;
  188.         case OldCharging:             StartShooting(OLD);        break;
  189.         case Shooting:                    RestartCharging();        break;
  190.         case PullMoving:                if (pball_type==DiscThrower)    StopMoving();
  191.                                             break;
  192.         default:                            break;
  193.         }
  194.         break;
  195.  
  196.     case 3:        // Right Button
  197.         switch(mode) {
  198.         case PullMoving:                if (pball_type==DiscSlider)    break;
  199.         case Moving:                    StopMoving();                        break;
  200.         case Charging:
  201.         case OldCharging:
  202.         case Shooting:
  203.         case OldShooting:
  204.                                             GoUnlocked();            break;
  205.         default:                            break;
  206.         }
  207.         break;
  208.     }
  209. }
  210.  
  211.  
  212. void PBallTop::Update() {
  213.     switch(mode) {
  214.         case OldCharging:
  215.         case Charging:
  216.         case OldShooting:
  217.         case Shooting:
  218.             charge += charge_increment*(current_time-last_chargestep);
  219.             last_chargestep=current_time;
  220.             next_chargestep=last_chargestep+g->GetChargeGranularity();
  221.  
  222. #ifndef __TURBOC__
  223. #ifdef STATISTICS
  224. {            char    buffer[30];
  225.             sprintf( buffer, "Charge: %5.1f%%", (double)(charge/g->GetMaxCharge()*100.0) );
  226.             showinfo(PointerInfo,buffer);
  227. }
  228. #endif
  229. #endif
  230.             MoveAimingTool();        // Ausgabe des Ladezustands
  231.             if (mode==OldCharging) {
  232.                 if (charge>g->GetMaxCharge())    {
  233.                         charge = charge / 6.0;    // ▄berlauf -> Kraft reduzieren
  234.                         MoveAimingTool();            // Ausgabe des Ladezustands
  235.                         StartShooting(OLD);
  236.                 }
  237.             }
  238.             else {
  239.                 if (charge<RealZero) {
  240.                         Shoot();
  241.                 }
  242.                 else if (charge>g->GetMaxCharge())    charge=charge/2.0;
  243.             }
  244.             break;
  245.  
  246.         case PullMoving:
  247.             if (target_time<current_time) {
  248.                 WarpRecalc();
  249.             }
  250.             break;
  251.  
  252.         case LockedQueue:
  253.             if (lock_time<current_time) {
  254.                 GoUnlocked();
  255.             }
  256.             break;
  257.         default:    
  258.             break;
  259.     }
  260. }
  261.  
  262. void PBallTop::Redraw() {
  263.     tool_is_visible      = 0;
  264.     valid_queue_position = 0;
  265.     if (mode&UsingTool)            DrawAimingTool();
  266.     RedrawPointer();
  267. }
  268.  
  269.  
  270. #if (0)
  271. void PBallTop::SetIndirect() {
  272.     mode=Indirect;
  273. #if (0)
  274.     Draw();
  275.     m = cue->R()*R()*R()/8;
  276.     Draw();
  277. #endif
  278. #if (SHOW_MODE)
  279.     showinfo(PointerInfo,"Indirect       ");
  280. #endif
  281. }
  282.  
  283. void PBallTop::SetDirect() {
  284.     mode=Direct;
  285.     target_time=NO_TARGET;
  286.     Draw();
  287.     m = 1;
  288.     Draw();
  289.     PointerMoveTo( dest );
  290.     Warp( dest );
  291. #if (SHOW_MODE)
  292.     showinfo(PointerInfo,"Direct         ");
  293. #endif
  294. }
  295. #endif
  296.  
  297. Ball *PBallTop::FindCueBall() {
  298. Ball    *best;
  299. Real    min_dist=Ball::FindClosest(cue,dest,&best);
  300.  
  301.     if (min_dist<2.*best->R()) {
  302.         if (g->IsSelectable(best))        return best;
  303.     }
  304.     return 0;
  305. }
  306.  
  307.  
  308. void PBallTop::StartAiming( Ball *cueball ) {
  309.     if (cueball)                    cue = cueball;
  310.     else                                cue = FindCueBall();        // naechste Kugel suche
  311.     if (cue&&cue->Lock(this))    cue=0;                        // Lock erlaubt
  312.     if (!cue)                        return;
  313.  
  314.     mode    = Aiming;
  315.     aim_hint = 1;
  316.     charge  = 0.0;
  317.     StartAimingTool();
  318. #if (SHOW_MODE)
  319.     showinfo(PointerInfo,"Aiming         ");
  320. #endif
  321. }
  322.  
  323. void PBallTop::StartCharging( int old ) {
  324.     mode = (old)?OldCharging:Charging;
  325.     charge = 0.0;
  326.     if (old)     charge_increment = g->GetChargeSpeed();
  327.     else         charge_increment = g->GetChargeSpeed()/NEW_FACTOR;
  328.     last_chargestep  = current_time;
  329.     next_chargestep  = last_chargestep + g->GetChargeGranularity();
  330. #if (SHOW_MODE)
  331.     showinfo(PointerInfo,"Charging       ");
  332. #endif
  333. }
  334.  
  335. void PBallTop::RestartCharging() {
  336.     mode = Charging;
  337.     charge_increment = g->GetChargeSpeed();
  338. }
  339.  
  340. void PBallTop::StartShooting( int old ) {
  341.     save_charge = charge;
  342.     if (old)        charge_increment = -(charge/g->GetShootTime());
  343.     else            charge_increment = -(charge/g->GetShootTime()/NEW_FACTOR);
  344.     mode = (old)?OldShooting:Shooting;
  345. }
  346.  
  347. void PBallTop::Shoot() {
  348. Vec2    dir = dest-cue->P();
  349.  
  350.     if (!dir.IsZero()) {
  351.         g->ShootBall( cue );                // neuen Schu▀ anzeigen
  352.         cue->TellPressed();                // Pressed-Kugel dem Game-Objekt mitteilen
  353.         cue->ChgV(dir.Norm1()*save_charge);
  354.     }
  355.  
  356.     charge   = RealZero;                    // Damit der Queue sichtbar
  357.     EndAimingTool();                        // L÷schen (inkl. Vorausberechnung)
  358.  
  359.     default_cue->m = cue->m*4.;
  360.     ReleaseBall();                            // Verbindung zur Kugel abbrechen
  361.     cue = default_cue;
  362.     cue->idle = 0;
  363.     cue->SetP( q_end );                    // Hilfsball an Queue-Spitze
  364.     cue->ChgV( Vec2Zero );    // ... ohne Bewegung
  365.     cue->Lock(this);                        // ... Verfolgung einschalten
  366.  
  367.     aim_hint = 0;
  368.     mode = LockedQueue;
  369.     StartAimingTool();
  370.  
  371.     lock_time = current_time + LockedQueueDelay;        // nachgelocken
  372.  
  373. #if (SHOW_MODE)
  374.     showinfo(PointerInfo, "LockedQueue    " );
  375. #endif
  376. }
  377.  
  378.  
  379. void PBallTop::StartPullMoving( Ball *cueball ) {
  380.     if (cueball)                    cue = cueball;
  381.     else                                cue = FindCueBall();
  382.     if (cue&&cue->Lock(this))    cue=0;                        // Lock erlaubt
  383.     if (!cue)            return;
  384.  
  385.     mode = PullMoving;
  386. #if (SHOW_MODE)
  387.     showinfo(PointerInfo,"PullMoving     ");
  388. #endif
  389. }
  390.  
  391.  
  392. void PBallTop::StartMoving( Ball *cueball ) {
  393.     if (cueball)        cue = cueball;
  394.     else                    cue = FindCueBall();
  395.     if (!cue)            return;
  396.  
  397.     mode = Moving;
  398.     cue->idle = 1;        // Ball vom Tisch
  399.     cue->SetP(dest);
  400.     cue->ChgV(Vec2Zero);
  401. #if (SHOW_MODE)
  402.     showinfo(PointerInfo,"Moving         ");
  403. #endif
  404. }
  405.  
  406.  
  407. void PBallTop::StopMoving() {
  408.     cue->idle = 0;                                // Ball wieder auf dem Tisch
  409.     if (pball_type!=DiscThrower)
  410.                     cue->ChgV(Vec2Zero);        // Ball gegebenenfalls stoppen
  411.     GoUnlocked();
  412. }
  413.  
  414. void PBallTop::ReleaseBall() {
  415.     if (cue) {
  416.         cue->Unlock(this);
  417.         if (cue==default_cue) {
  418.             default_cue->idle = 1;
  419.             default_cue->SetP( Vec2Zero );
  420.             default_cue->ChgV( Vec2Zero );
  421.         }
  422.         cue=0l;
  423.     }
  424. }
  425.  
  426. void PBallTop::GoUnlocked() {
  427.     if (mode&UsingTool) {
  428.         EndAimingTool();        // L÷schen des Aiming-Tools
  429.         ReleaseBall();
  430.     }
  431.     else if (mode==Moving) {
  432.         Ball    *next;
  433.         Real    dist = Ball::FindClosest( cue, dest, &next );
  434.  
  435.         if (next&&dist<next->R()+cue->R()) {
  436.             Vec2 dir = (next->P()-cue->P()).Norm1();
  437.             next->ChgV( dir*(next->R()+cue->R()-dist)*4.0 );
  438.             cue->ChgV( -dir*(next->R()+cue->R()-dist)*4.0 );
  439.         }
  440.         cue=0l;
  441.     }
  442.     else if (mode==PullMoving) {
  443.         target_time = NO_TARGET;
  444.         ReleaseBall();
  445.     }
  446.  
  447.     mode = Unlocked;
  448. #if (SHOW_MODE)
  449.     showinfo(PointerInfo,"Unlocked       ");
  450. #endif
  451. }
  452.  
  453.  
  454. void PBallTop::PreCalc() {
  455. Vec2    dir = dest-cue->P();
  456.  
  457.     ncalc_pos=0;
  458.     balls_dir=Vec2Zero;
  459.  
  460.     if (!dir.IsZero()) {
  461.         Vec2    p_save = cue->P();
  462.         Vec2    v_save = cue->v;
  463.         Real        slowstep_save = cue->next_slowstep;
  464.         Real        next_time;
  465.         Real        min_time;
  466.         Object    *hit_object;
  467.  
  468.         cue->v = dir.Norm1();
  469.         Object *obj;
  470.  
  471.         for (ncalc_pos=0;ncalc_pos<MAX_PREPOS;) {
  472.             min_time   = MAX_TIME;
  473.             hit_object = 0;
  474.             for (obj=Object::stat_queue; obj; obj=obj->Object::next ) {
  475.                 if (obj!=cue) {
  476.                     next_time = obj->HitFromBall(cue);
  477.                     if ( next_time < min_time ) {
  478.                             min_time   = next_time;
  479.                             hit_object = obj;
  480.                     }
  481.                 }
  482.             }
  483.             if (min_time<MAX_TIME) {
  484.                 cue->p = cue->P() + min_time*cue->V();
  485.             }
  486.             else {
  487.                 break;
  488.             }
  489.  
  490.             calc_pos[ncalc_pos++] = cue->P();
  491.             if (hit_object->dyn_id!=-1) {
  492.                 if (hit_object->dyn_id>=0) {
  493.                     Vec2 e = cue->V().Norm1();
  494.                     e.Split(((Ball*)hit_object)->P()-cue->P(),&balls_dir,&my_dir);
  495.                 }
  496.                 break;
  497.             }
  498.             cue->next_slowstep = SUPPRESS_SLOWSTEP;
  499.             hit_object->CollideWithBall(cue);
  500.         }
  501.  
  502.         cue->next_slowstep = slowstep_save;
  503.         cue->v = v_save;
  504.         cue->p = p_save;
  505.     }
  506. }
  507.  
  508.  
  509.  
  510.  
  511. #define    CHARGE_LEN    20.0
  512. #define    QUEUE_LEN    50.0
  513.  
  514. void PBallTop::DrawQueue() {
  515.     if (valid_queue_position) {
  516. #ifdef __TURBOC__
  517.         DrawLine(q_s1_s,q_end_s);
  518.         DrawLine(q_s2_s,q_end_s);
  519. #else
  520.         FillPoly( 3, &q_end_s, &q_s1_s, &q_s2_s );
  521. #endif
  522.     }
  523. }
  524.  
  525. void PBallTop::StartQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
  526.     q_end_s = end;
  527.     q_s1_s  = s1;
  528.     q_s2_s  = s2;
  529.     valid_queue_position = 1;
  530.     DrawQueue();
  531. }
  532.  
  533. void PBallTop::MoveQueue(const Vec2 &end, const Vec2 &s1, const Vec2 &s2) {
  534.     DrawQueue();
  535.     q_end_s = end;
  536.     q_s1_s  = s1;
  537.     q_s2_s  = s2;
  538.     valid_queue_position = 1;
  539.     DrawQueue();
  540. }
  541.  
  542. void PBallTop::EndQueue() {
  543.     DrawQueue();
  544.     valid_queue_position = 0;
  545. }
  546.  
  547. static void DrawArrow( const Vec2 &from, const Vec2 &dist ) {
  548. Vec2    to = from+8.0*dist*4.0;
  549. Vec2    pa = dist.TurnAngleDeg( 150.)*4.0;
  550. Vec2    pb = dist.TurnAngleDeg(-150.)*4.0;
  551.  
  552.     DrawLine(from,to);
  553.     DrawLine(to,to+pa);
  554.     DrawLine(to,to+pb);
  555. }
  556.  
  557.  
  558. void PBallTop::DrawAimingTool(int charge_only) {
  559. #ifdef __TURBOC__
  560.     Vec2 dest((aim_x-ox)/w2n,(aim_y-oy)/w2n);
  561. #else
  562.     Vec2 dest(aim_x/w2n,aim_y/w2n);
  563.     gc_current = gc_cursor;
  564. #endif
  565.     Vec2 dir= dest - aim_p;
  566.  
  567.     if (!dir.IsZero()) {
  568.         Real        len        = dir.Norm();
  569.         Vec2    dir_norm    = dir / len;        // <=> Norm1()
  570.     //    Vec2    mid        = aim_p+dir/2.0;
  571.         Vec2    off = dir_norm*QUEUE_LEN/50.0;
  572.         Vec2    off1=off.TurnLeft();
  573.         Vec2    off2=off.TurnRight();
  574.  
  575.         if (len<6.*aim_r)    hint_valid=0;
  576.  
  577.         if ((!aim_hint||!hint_valid)&&(aim_mode!=LockedQueue)) {
  578.             DrawLine(aim_p,dest);
  579.         }
  580.  
  581.         if (aim_mode==LockedQueue||((aim_mode&ChargeOrShoot)&&(aim_charge>=0.0))) {
  582.                         q_end        = aim_p
  583.                         - dir_norm*(aim_r+aim_charge/g->GetMaxCharge()*CHARGE_LEN);
  584.             Vec2    q_start    = q_end - QUEUE_LEN*dir_norm;
  585.             Vec2    qs1 = q_start+off1;
  586.             Vec2    qs2 = q_start+off2;
  587.             if (!tool_is_visible) {
  588.                 if (valid_queue_position)    MoveQueue(q_end,qs1,qs2);
  589.                 else                                StartQueue(q_end,qs1,qs2);
  590.             }
  591.         }
  592.  
  593.         if (!charge_only && hint_valid) {
  594. #ifndef __TURBOC__
  595.             XSetLineAttributes(dpy,gc_cursor,0,LineOnOffDash,CapRound,JoinRound);
  596. #else
  597.             setlinestyle(DASHED_LINE,0,1);
  598. #endif
  599.             DrawLine(aim_p,calc_pos[0]);
  600.             for (int i=1;i<ncalc_pos;i++) {
  601.                 DrawLine(calc_pos[i-1],calc_pos[i]);
  602.             }
  603. #ifndef __TURBOC__
  604.             DrawCircle( calc_pos[i-1], aim_r );
  605.             XSetLineAttributes(dpy,gc_cursor,0,LineSolid,CapRound,JoinRound);
  606.             if ((!nohint_flag)&&(ncalc_pos<2)&&!balls_dir.IsZero()) {
  607. #else
  608.             setlinestyle(SOLID_LINE,0,1);
  609.             if ((!nohint_flag)&&(ncalc_pos<3)&&!balls_dir.IsZero()) {
  610. #endif
  611.                 DrawArrow( calc_pos[ncalc_pos-1], balls_dir );
  612.                 DrawArrow( calc_pos[ncalc_pos-1], my_dir );
  613.             }
  614.         }
  615.     }
  616.     tool_is_visible ^= 1;    // toggle state
  617. }
  618.  
  619.  
  620. void PBallTop::SaveAimingState() {
  621.     aim_p            = cue->P();    // Werte retten
  622.     aim_r            = cue->R();
  623.     aim_x            = x_old;
  624.     aim_y            = y_old;
  625.     aim_charge    = charge;
  626.     int_charge    = (int)(charge/2.);
  627.     aim_mode    = mode;
  628. }
  629.  
  630.  
  631. void PBallTop::StartAimingTool() {
  632.     if (aim_hint) {
  633.         PreCalc();
  634.         hint_valid=1;
  635.     }
  636.     else {
  637.         hint_valid=0;
  638.     }
  639.     SaveAimingState();
  640.     DrawAimingTool();
  641. }
  642.  
  643. void PBallTop::MoveAimingTool() {
  644. int    same_pos = (mode==aim_mode)&&((aim_p==cue->P())&&(aim_x==x_old)&&(aim_y==y_old));
  645.  
  646.     if (same_pos&&((int)(charge/2.)==int_charge))
  647.         return;                // keine ─nderung
  648.  
  649.     DrawAimingTool(same_pos);        // L÷schen der alten Zeichnung
  650.     if (aim_hint) {
  651.             if (!same_pos||!hint_valid)    PreCalc();
  652.             hint_valid=1;
  653.     }
  654.     else    hint_valid=0;
  655.     SaveAimingState();
  656.     DrawAimingTool(same_pos);        // Neuzeichnen
  657. }
  658.  
  659. void PBallTop::EndAimingTool() {
  660.     DrawAimingTool();
  661.     EndQueue();
  662. }
  663.  
  664.  
  665. #ifndef __TURBOC__
  666. #    include "xpball.C"
  667. #else
  668. #    include "dospball.C"
  669. #endif
  670.